﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace CTC
{
    /// <summary>
    /// 主控端(卡头)
    /// </summary>
    public class CTCMaster : CTCDevice
    {

        #region 事件
        /// <summary>
        /// 连接状态变化函数
        /// </summary>
        public event CTCEvent.ClientConnStateEvent OnStateChange;

        /// <summary>
        /// 从设备状态改变
        /// </summary>
        public event CTCEvent.ByteDataEvent OnSlaveStateChange;

        /// <summary>
        /// 抄表回应
        /// </summary>
        public event CTCEvent.ByteDataEvent OnMeterData;

        /// <summary>
        /// 实体数据(实物币,实物票)上报
        /// </summary>
        public event CTCEvent.ByteDataEvent OnPhysicalReport;
        /// <summary>
        /// 查询每局投币回应
        /// </summary>
        public event CTCEvent.ByteDataEvent OnQueryCoin;

        /// <summary>
        /// 氛围事件
        /// </summary>
        public event CTCEvent.ByteDataEvent OnAtmosphere;

        /// <summary>
        /// 握手成功
        /// </summary>
        public event CTCEvent.ByteDataEvent OnHandshake;
        /// <summary>
        /// 卡头投币回应
        /// </summary>
        public event CTCEvent.ByteDataEvent OnCoinInRsp;
        /// <summary>
        /// 游戏有奖励给玩家(游戏机到卡头)
        /// </summary>
        public event CTCEvent.ByteDataEvent OnReward;
        #endregion

        public CTCMaster(String comName, UInt32 prudoctID, byte[] deviceID) : base(0, comName, 38400)
        {
            MasterDeviceId = deviceID;
            MasterProductId = prudoctID;
            SlaveCTCversion = CTC_SLAVE_VERSION;
            DeviceConnState = CTCClientConnState.Offline;
        }
        public override void Open()
        {
            base.Open();
            if (SerialConnected)
            {
                SendHandshake();
            }
        }

        protected override void HandleTimeOut()
        {
            if (isHandshake)
            {
                SendHeartbeat();
            }
            else
            {
                SendHandshake();
            }
        }
        protected override void HandleOffline()
        {
            base.HandleOffline();
            OnStateChange?.Invoke(Player, DeviceConnState);
        }
        protected override void HandleSendFail(byte ackState, DataPacket dataPacket)
        {
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_HANDSHAKE))
            {
                LogE("握手发送失败,10秒后重试中");
                ReconnectNumber++;
            }
            else
            {
                base.HandleSendFail(ackState, dataPacket);
            }
        }

        protected override void HandleReceive(DataPacket dataPacket)
        {
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_HANDSHAKE_RSP))
            {//握手应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_HANDSHAKE_RSP)
                {
                    LogE(String.Format("CTC Handshake Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                isHandshake = true;
                ReconnectNumber = 0;
                DeviceConnState = CTCClientConnState.Connected;
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                SlaveCTCversion = CRC.BytesToUint16(dataPacket.Data[20], dataPacket.Data[21]);
                OnStateChange?.Invoke(Player, DeviceConnState);
                OnHandshake?.Invoke(Player, dataPacket.Data);
                return;
            }
            //如果没有完成握手就不处理其它指令
            if (!isHandshake)
            {
                return;
            }
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_HEARTBEAT_RSP))
            {//心跳应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_HEARTBEAT_RSP)
                {
                    LogE(String.Format("CTC Heartbeat Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                byte slaveState = dataPacket.Data[0];
                UInt16 slaveError = CRC.BytesToUint16(dataPacket.Data[1], dataPacket.Data[2]);
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                if (slaveState != SlaveState || slaveError != SlaveErrorCode)
                {
                    SlaveState = slaveState;
                    SlaveErrorCode = slaveError;
                    OnSlaveStateChange?.Invoke(Player, dataPacket.Data);
                }
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_METER_DATA))
            {//读表应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_METER_DATA_RSP)
                {
                    LogE(String.Format("CTC Meter Data Rsp Length Error:{0}:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnMeterData?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_COIN_IN_RSP))
            {//投币结果
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_COIN_IN_RSP)
                {
                    LogE(String.Format("CTC Coin In Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnCoinInRsp?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_QUERY_COIN_RSP))
            {//查询单价结果
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_QUERY_COIN)
                {
                    LogE(String.Format("CTC Query Coin Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnQueryCoin?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_ATMOSPHEREEVENT))
            {//氛围灯事件
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_ATMOSPHERE_EVENT)
                {
                    LogE(String.Format("CTC Atmosphere Event Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;

                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnAtmosphere?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_REWARD))
            {//游戏奖励
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_REWARD)
                {
                    LogE(String.Format("CTC Game Reward Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnReward?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_PHYSICAL_REPORT))
            {//实物奖励结果
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_PHYSICAL_REPORT)
                {
                    LogE(String.Format("CTC Physical Report Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnPhysicalReport?.Invoke(Player, dataPacket.Data);
            }
            else
            {
                // 不支持的指令
                SendACK(dataPacket, CTCMsg.CTC_ACK_UNSUPPORTED);
            }
        }

        /// <summary>
        /// 握手
        /// </summary>
        protected void SendHandshake()
        {
            LogI("发送握手");
            byte[] data = new byte[22];
            Array.Copy(CRC.Uint32ToBytes(MasterProductId), 0, data, 0, 4);
            Array.Copy(MasterDeviceId, 0, data, 4, 16);
            Array.Copy(CRC.Uint16ToBytes(CTC_MASTER_VERSION), 0, data, 20, 2);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_HANDSHAKE, SendTransactionId++, data));
        }
        /// <summary>
        /// 发送抄表指令
        /// </summary>
        public void SendReadMeter()
        {
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_METER_READ, SendTransactionId++, null));
        }
        /// <summary>
        /// 发送心跳
        /// </summary>
        protected override void SendHeartbeat()
        {
            if (DeviceConnState == CTCClientConnState.Connected)
            {
                LogI("发送心跳");
                Send(new DataPacket(Player, CTCMsg.CTC_CMD_HEARTBEAT, SendTransactionId++, MasterState));
            }
        }
        /// <summary>
        /// 发送投币
        /// </summary>
        /// <param name="number"></param>
        public void SendInCoin(UInt32 number, byte[] memberID, byte[] orderNo)
        {
            byte[] data = new byte[36];
            Array.Copy(CRC.Uint32ToBytes(number), 0, data, 0, 4);
            Array.Copy(memberID, 0, data, 4, 16);
            Array.Copy(orderNo, 0, data, 20, 16);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_COIN_IN, SendTransactionId++, data));
        }
        /// <summary>
        ///  通道选择
        /// </summary>
        /// <param name="c0">彩票通道	0=实物,1=电子,2=暂时不出</param>
        /// <param name="c1">蓝票通道	0=实物,1=电子,2=暂时不出</param>
        public void SendSelectChannel(byte c0, byte c1)
        {
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_CHANNEL_SEL, SendTransactionId++, c0, c1));
        }
        /// <summary>
        /// 查询每局币数
        /// </summary>
        public void SendQueryCoinPrice()
        {
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_QUERY_COIN, SendTransactionId++, null));
        }
        /// <summary>
        /// 设置每局部币数
        /// </summary>
        /// <param name="number"></param>
        public void SendSetCoinPrice(UInt16 number)
        {
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_SET_COIN, SendTransactionId++, CRC.Uint16ToBytes(number)));
        }
        /// <summary>
        /// 变更状态
        /// </summary>
        /// <param name="state">0=正常可用,1=等待配网,2=连接门店服务异常,3=网络无法连接,4=正在联网</param>
        public void ChangeState(byte state)
        {
            MasterState = state;
            SendHeartbeat();
        }
    }
}
